package org.calconnect.icalproofing;

/**
 *	<p>ErrorHandler.</p>
 *
 *	<p>Utility routines for handling service-related errors and returning
 *	representations of these errors to the client.</p>
 *
 *	<p><em>Last checked in by: $Author: aron $ at $Date: 2008-10-23 15:23:16 -0700 (Thu, 23 Oct 2008) $ (UTC)</em></p>
 *
 *	@author Aron Roberts
 *	@version $Revision: 504 $
 *
 */

/*
 * Copyright 2005-2007 Noelios Consulting.
 * 
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License (the "License"). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at
 * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL HEADER in each file and
 * include the License file at http://www.opensource.org/licenses/cddl1.txt If
 * applicable, add the following below this CDDL HEADER, with the fields
 * enclosed by brackets "[]" replaced with your own identifying information:
 * Portions Copyright [yyyy] [name of copyright owner]
 */
 
import org.apache.log4j.Logger;

import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.restlet.resource.Representation;
import org.restlet.resource.ResourceException;
import org.restlet.resource.StringRepresentation;
import org.restlet.service.StatusService;

public class ErrorHandler extends StatusService {

	// Class variables
	
	// The name of this class
	final static String CLASSNAME = ErrorHandler.class.getName();

	// Create a log4j 'logger' for this class, based on the class name
	// This allows us to identify which log events are coming from the
	// methods of this class	
	private static Logger logger = Logger.getLogger( CLASSNAME );
	
	final static boolean ENABLED = true;
	final static boolean OVERWRITE = true;
	
	// Instance variables
	private String errmsg;
	private Status status;
	private Request request;
	private Exception exception;
	private MediaType mediaType;

	/////////////////////////////////////////////////////////////////
	/**
	 *	Class constructor (no argument).
	 */
	public ErrorHandler() {
	
		// Set the default behavior of this status service:
		// - Enabled
		// - Always overwrite the current HTTP entity body.
		super( ENABLED );
		super.setOverwrite( OVERWRITE );
		
		logger.debug( "In no-argument constructor for " + CLASSNAME + "...");
		
	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Class constructor.
	 *
	 *	@param		status	The protocol status of the error.
	 */
	public ErrorHandler( Status status ) {
	
	  logger.debug( "In constructor( String, Status ) for class " + CLASSNAME );
	 	
	 	this.status = status;
	  
	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Class constructor.
	 *
	 *	@param		status	The HTTP protocol status of the error.
	 *
	 *	@param		request	An HTTP request.
	 */
	public ErrorHandler( Status status, Request request ) {
	
	  logger.debug( "In constructor( String, Request ) for class " + CLASSNAME );
	 	
	 	this.status = status;
	 	this.request = request;
	  
	}
	
	/////////////////////////////////////////////////////////////////
	/**
	 *	Class constructor.
	 *
	 *	@param		re			A ResourceException
	 */
	public ErrorHandler( ResourceException re ) {
	
	  logger.debug( "In constructor( ResourceException ) for class " + CLASSNAME );

		this.errmsg = re.getMessage();
	 	this.status = re.getStatus();
	 	this.exception = re;
	 	
	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Class constructor.
	 *
	 *	@param		re			A ResourceException
	 *
	 *	@param		request	An HTTP request.
	 */
	public ErrorHandler( ResourceException re, Request request ) {
	
	  logger.debug( "In constructor( ResourceException ) for class " + CLASSNAME );

		this.errmsg = re.getMessage();
	 	this.status = re.getStatus();
	 	this.exception = re;
	 	this.request = request;
	 	
	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Class constructor.
	 *
	 *	@param		errmsg	An error message.
	 *
	 *	@param		status	The HTTP protocol status of the error.
	 */
	public ErrorHandler( String errmsg, Status status ) {
	
	  logger.debug( "In constructor( String, Status ) for class " + CLASSNAME );
	 	
	 	this.errmsg = errmsg;
	 	this.status = status;
	  
	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Class constructor.
	 *
	 *	@param		errmsg	An error message.
	 *
	 *	@param		status	The HTTP protocol status of the error.
	 *
	 *	@param		request	An HTTP request.
	 */
	public ErrorHandler( String errmsg, Status status, Request request ) {
	
	  logger.debug( "In constructor( String, Status, Request ) for class " + CLASSNAME );
	 	
	 	this.errmsg = errmsg;
	 	this.status = status;
	 	this.request = request;
	  
	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Class constructor.
	 *
	 *	@param		errmsg			An error message.
	 *
	 *	@param		status			The HTTP protocol status of the error.
	 *
	 *	@param		exception		An underlying exception pertinent to the error, if any.
	 */
	public ErrorHandler( String errmsg, Status status, Exception exception ) {
	
	  logger.debug( "In constructor( String, Status, Exception ) for class " + CLASSNAME );

	 	this.errmsg = errmsg;
	 	this.status = status;
	 	this.exception = exception;
	 	
	 	logger.debug( "exception message = " + exception.getMessage() );
	 	
	 	// Set the default media type for error messages
	 	setMediaType( MediaType.TEXT_PLAIN );

	 	// @todo: revisit whether performing automatic logging of each error created,
	 	// which occurs here, continues to make sense.
	 	log();
	 		 	
	}
	
	/////////////////////////////////////////////////////////////////
	/**
	 *	Return a representation of the error.
	 *
	 *	@param		request		An HTTP request.
	 *
	 *	@param		response	An HTTP response.
	 *
	 *  @return		A representation of the error.
	 */
	public Representation getRepresentation( Request request, Response response ) {
	
		logger.debug( "In getRepresentation( Request, Response ) ... in class " + CLASSNAME );
		
		return getRepresentation( this.status, request, response );
		
	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Return a representation of the error.
	 *
	 *	@param		status		The HTTP protocol status of the error.
	 *
	 *	@param		request		An HTTP request.
	 *
	 *	@param		response	An HTTP response.
	 *
	 *  @return		A representation of the error.
	 */
	@Override
	public Representation getRepresentation( Status status, Request request, Response response ) {
	
		logger.debug( "In getRepresentation( String, Status, Exception ) ... in class " + CLASSNAME );

	 	this.status = status;
	 	this.request = request;

		return new StringRepresentation( toString(), getMediaType() );

	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Return a Status representation of the current error.
	 *
	 *	@param		throwable		An exception or other throwable condition.
	 *
	 *	@param		request			An HTTP request.
	 *
	 *	@param		response		An HTTP response.
	 *
	 *  @return		A Status representation of the current error.
	 */
	@Override
	public Status getStatus( Throwable throwable, Request request, Response response ) {
	
		logger.debug( "In getRepresentation( Throwable, Status, Response ) ... in class " + CLASSNAME );

	 	this.exception = (Exception) throwable;
	 	this.request = request;
	 			
		return getStatus();

	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Return a consolidated error message in a standard format.
	 *
	 *  @return		A consolidated, formatted error message.
	 */
  public String getErrorMessage() {
  
    logger.debug( "In getErrorMessage( String ) ... in class " + CLASSNAME );
    
	  StringBuffer sb = new StringBuffer();
	  
	  if ( this.errmsg != null ) {
	  	sb.append( this.errmsg );
	  } else if ( this.status != null ) {
	 		sb.append( this.status.getDescription() );
	  } else if ( this.exception != null && this.exception.getCause() != null ) {
	  	sb.append( this.exception.getCause().getMessage() );
	  } else if ( this.exception != null ) {
	  	sb.append( this.exception.getMessage() );
	 	}
	  
	 	return sb.toString();
		
	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Sets the media type of the error message.
	 *
	 *	@param		mediaType		The media type of the error message.
	 */
	public void setMediaType( MediaType mediaType ) {
	
		this.mediaType = mediaType;
	
	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Returns the media type of the error message
	 *	(to be returned as the entity body of the response).
	 *
	 *	@return		The media type of the error message.
	 */
	public MediaType getMediaType() {
	
		return this.mediaType;
	
	}
	
	/////////////////////////////////////////////////////////////////
	/**
	 *	Log the error message.
	 */
  public void log() {
  
    logger.debug( "In log() ... in class " + CLASSNAME );

	 	// @todo Provide some controls over logging, such as changing the log level.
	 	logger.warn( getErrorMessage() );
	 	
	}
	  
	/////////////////////////////////////////////////////////////////
	/**
	 *	Return a Status object which encapsulates the error, including the
	 *  error message, protocol status, and original exception message, if any.
	 *
	 * @return		An Status object which encapsulates the error.
	 */
  public Status getStatus() {
  
    logger.debug( "In getStatus() ... in class " + CLASSNAME );

		if ( this.status != null ) {
 			return new Status( this.status, getErrorMessage() );
 		// If no Status object was stored in this ErrorHandler object,
 		// return a DS error.
 		} else {
 			return new Status( Status.SERVER_ERROR_INTERNAL, "An internal error has occurred." );
 		}
 		
 	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Return a representation of the error message, intended
	 *	for use as the entity body of the response to the client.
	 *
	 * @return		A representation of the error message.
	 */
  public Representation getEntity() {

		// return new StringRepresentation( getErrorMessage(), getMediaType() );
		return new StringRepresentation( toString(), getMediaType() );

	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Return an exception which encapsulates the error, including the
	 *  error message, protocol status, and original exception message, if any.
	 *
	 * @return		An exception which encapsulates the error.
	 */
  public ResourceException getException() {
  
    logger.debug( "In getException() ... in class " + CLASSNAME );

 		return new ResourceException( new Status( this.status, getErrorMessage() ) );
 		
 	}

	/////////////////////////////////////////////////////////////////
	/**
	 *	Returns a string representation of the error.
	 *
	 *	@return		A string representation of the error.
	 */
	public String toString() {

		logger.debug( "In toString() for " + CLASSNAME + "...");
		
		StringBuffer sb = new StringBuffer( "" );

/*		
		sb.append( "Status code: " );
		if ( StringUtils.isNotEmpty( getStatusCode( getStatus() ) ) ) {
			sb.append( getStatusCode( getStatus() ) );
		}
		sb.append( "\n" );
*/

		sb.append( "Status message: " );
		if ( StringUtils.isNotEmpty( getErrorMessage() ) ) {
			sb.append( getErrorMessage() );
		}
		sb.append( "\n" );
			
		if ( this.request != null ) {
			sb.append( "Request: " + this.request.getResourceRef().toString() + "\n" );
		}
		
		return sb.toString();
		
	}

 	/////////////////////////////////////////////////////////////////
	/**
	 *	Returns a string representing the HTTP status code, including
	 *	its code number and descriptive name.
	 *
	 *	@param		A Status object.
	 *
	 *	@return		A string representing the status code.
	 */
	public String getStatusCode( Status status ) {
	
		logger.debug( "In getStatusCode( Status ) in class " + CLASSNAME + "...");
		
		return
			StringUtils.intToString( status.getCode() ) +
			" " +
			"(" + status.getName() + ")";
		
	}
  
	/////////////////////////////////////////////////////////////////

  public static void main( String[] args ) {

		// Display descriptive information about this class

  	System.out.println( CLASSNAME );
  	System.out.println( "$Revision: 504 $" );
  	System.out.println( "$Date: 2008-10-23 15:23:16 -0700 (Thu, 23 Oct 2008) $" );

		Exception be = new Exception( "bar" );
		try {
			logger.warn( be.getMessage() );
			throw be;
		} catch ( Exception e ) {
 			String errmsg = "msg";
			ErrorHandler errorHandler =
  			new ErrorHandler( errmsg, Status.CLIENT_ERROR_UNAUTHORIZED, e );
  		logger.warn( errorHandler.getErrorMessage() );
  		logger.warn( errorHandler.getException().getMessage() );
 		}
  	
	} // end of main() method


} // end of class


